package net.ncraft.mc.ezframework.core.handler.database;
import com.zaxxer.hikari.HikariDataSource;
import net.ncraft.mc.ezframework.core.annotations.database.SQL;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class WrapHandler implements InvocationHandler {

    private final String dataSourceName;

    public WrapHandler(String dataSourceName) {
        this.dataSourceName = dataSourceName;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        HikariDataSource dataSource = DatabaseHandler.getDataSource(dataSourceName);
        if (dataSource == null) {
            return null;
        }

        if (!method.isAnnotationPresent(SQL.class)) {
            return null;
        }

        SQL sqlAnn = method.getAnnotation(SQL.class);
        Class<?> returnType = method.getReturnType();
        String sql = sqlAnn.value();

        try {
            Connection connection = dataSource.getConnection();
            PreparedStatement statement = connection.prepareStatement(sql);


            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (arg instanceof String) {
                    statement.setString(1, (String) arg);
                }else if (arg instanceof Integer) {
                    statement.setInt(1, (Integer) arg);
                }else if (arg instanceof Long) {
                    statement.setLong(1, (Long) arg);
                }else if (arg instanceof Double) {
                    statement.setDouble(1, (Double) arg);
                }else if (arg instanceof Float) {
                    statement.setFloat(1, (Float) arg);
                }else if (arg instanceof Boolean) {
                    statement.setBoolean(1, (Boolean) arg);
                }else if (arg instanceof Byte) {
                    statement.setByte(1, (Byte) arg);
                }else if (arg instanceof Short) {
                    statement.setShort(1, (Short) arg);
                }else if (arg instanceof Character) {
                    statement.setString(1, String.valueOf(arg));
                }else if (arg instanceof byte[]) {
                    statement.setBytes(1, (byte[]) arg);
                }
            }

            if (returnType.equals(Void.class)) {
                statement.execute();
                return null;
            }

            ResultSet resultSet = statement.executeQuery();
            Object o = returnType.newInstance();
            while (resultSet.next()) {

                Field[] objFields = o.getClass().getDeclaredFields();
                for (Field field : objFields) {
                    field.setAccessible(true);
                    field.set(o, resultSet.getObject(field.getName()));
                }
            }

            return o;

        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }
}
